package be.khl.kevinvranken.breekuit;

import be.khl.kevinvranken.breekuit.entiteiten.*;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;

import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.WindowConstants;

public class Spel extends Canvas {//Indien we gebruik maken van JPanel hebben we meer flicker dan Koen Crucke en Luc Appermont samen

    private int level = 1;
    private static int maxLevels = 5;
    private BufferStrategy strategy; //Anti flicker
    private boolean nietGepauseerd = true;
    private List<Entiteit> entiteiten = new ArrayList<Entiteit>();
    private List<Entiteit> entiteitenteVerwijderen = new ArrayList<Entiteit>();
    private Entiteit paddle; //De paddle onderaan. Hebben we zoveel nodig dat we ook ene apparte verwijzing bijhouden
    private Speelveld speelveld; //Houden we uit de lijst met entiteiten
    private double paddleSnelheid = 500;
    protected int resterendeBlokken; //Aantal blokken om te bepalen of we een leven hebben uitgespeeld
    private int resterendeBallen;
    private int extraBallen; //voor wanneer de extra bal upgrade wordt geactiveerd
    private String message = ""; //fg
    private boolean verderGaanKnopActieNodig = true;
    private boolean linksGedrukt = false;
    private boolean rechtsGedrukt = false;
    private boolean spatieKnop = false; // de actieknop
    private ScorePaneel scorePaneel = new ScorePaneel();
    private boolean levelInOpbouw = true;

    public Spel() {
        // create a frame to contain our game
        JFrame container = new JFrame("Breek Uit!");

        // Het paneel ophalen en enkele settings doen
        JPanel paneel = (JPanel) container.getContentPane();
        paneel.setPreferredSize(new Dimension(1000, 600));
        paneel.setBackground(Color.BLACK);
        paneel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));

        // Grootte van het canvas
        setBounds(0, 0, 800, 600);
        paneel.add(this);
        paneel.add(scorePaneel);

        setIgnoreRepaint(true); //dit doen we zelf beter


        // Menubar voor JFrame
        JMenuBar menuBar = new JMenuBar();
        JMenu bestandMenu = new JMenu("Bestand");

        // Menus wat inhoud geven
        JMenuItem nieuwActie = new JMenuItem(new NieuwActie(this));
        JMenuItem sluitenActie = new JMenuItem(new SluitenActie());
        bestandMenu.add(nieuwActie);
        bestandMenu.add(sluitenActie);
        menuBar.add(bestandMenu);
        container.setJMenuBar(menuBar);
        container.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        container.setResizable(false);
        container.setVisible(true);


        addKeyListener(new InvoerAfhandeling(this)); //Toetsluisteringen afhandelen

        // Pijltjes laten werken :)
        requestFocus();

        //Zondere dubbele buffer krijgen we meer flicker als Get Ready
        createBufferStrategy(2);
        strategy = getBufferStrategy();

        entiteitenOpzetten();
        container.pack();
    }

    /**
     * Start a fresh game, this should clear out any old data and
     * create a new set.
     */
    public void startGame() {
        getEntiteiten().clear();
        entiteitenOpzetten();
        setResterendeBallen(3);
        setExtraBallen(0);
        scorePaneel.resetScore();

        setLinksGedrukt(false);
        setRechtsGedrukt(false);
        setSpatieKnop(false);
    }

    private synchronized void entiteitenOpzetten() {
        levelInOpbouw = true;
        boolean knopactie = isVerderGaanKnopActieNodig();
        paddle = new Paddle(this, "sprites/paddle.gif", 370, 550);
        getEntiteiten().add(paddle);

        resterendeBlokken = 0;

        maakLevel.maak(this, level);

        //Eerstse bal maken
        Bal bal = new Bal(this, "sprites/bal.gif", 400, 300);
        getEntiteiten().add(bal);
        /**getEntiteiten().add(new Bal(this, "sprites/bal.gif", 50, 30));
        getEntiteiten().add(new Bal(this, "sprites/bal.gif", 50, 30));
        getEntiteiten().add(new Bal(this, "sprites/bal.gif", 50, 30));
        getEntiteiten().add(new Bal(this, "sprites/bal.gif", 50, 30));
        getEntiteiten().add(new Bal(this, "sprites/bal.gif", 50, 30));
        getEntiteiten().add(new Bal(this, "sprites/bal.gif", 50, 30));
        getEntiteiten().add(new Bal(this, "sprites/bal.gif", 50, 30));
        getEntiteiten().add(new Bal(this, "sprites/bal.gif", 50, 30));
        getEntiteiten().add(new Bal(this, "sprites/bal.gif", 50, 30)); **///stress-test
        levelInOpbouw = false;
        setVerderGaanKnopActieNodig(knopactie);
    }

    public void removeEntity(Entiteit entity) {
        entiteitenteVerwijderen.add(entity);
    }

    public void spelerGestorven() {
        message = "Hemeltje, al je ballen zijn op :( Nog een keer ?";
        setVerderGaanKnopActieNodig(true);
        scorePaneel.resetScore();
    }

    public void spelerGewonnen() {
        message = "Goed zo! Het spel is uitgespeeld! Je bent een echte winnaar";
        setVerderGaanKnopActieNodig(true);
    }

    public void spelerNieuwLevel() {
        message = "Goed zo! Je u bereikte het volgende level. Wat een held!";
        if (level + 1 > maxLevels) {
            spelerGewonnen();
            level = 1;
        } else {
            synchronized (this) {
                level++;
            }
        }
        setVerderGaanKnopActieNodig(true);
    }

    public void behandelVerliesBlok() {
        resterendeBlokken--;
        scorePaneel.setScore(10);

        if (resterendeBlokken == 0) {
            spelerNieuwLevel();
        }
    }

    /**
     * Afhandeling verlies van een bal
     */
    public void behandelVerliesBal() {
        if (extraBallen == 0) {
            setResterendeBallen(getResterendeBallen() - 1);
            if (getResterendeBallen() == 0) {
                spelerGestorven();
            } else {
                Bal bal = new Bal(this, "sprites/bal.gif", 400, 300);
                getEntiteiten().add(bal);
            }
        } else {
            extraBallen--;
        }
    }

    public void gameLoop() {
        long lastLoopTime = System.currentTimeMillis();

        while (nietGepauseerd) {
            long delta = System.currentTimeMillis() - lastLoopTime;
            lastLoopTime = System.currentTimeMillis();

            Graphics2D g = (Graphics2D) strategy.getDrawGraphics();

            if (!levelInOpbouw) {
                // alle entiteiten die vorige lus waren aangemerkt om verwijderd te worden verwijderen
                getEntiteiten().removeAll(entiteitenteVerwijderen);
                entiteitenteVerwijderen.clear();

                if (!isVerderGaanKnopActieNodig()) {
                    for (int i = 0; i < getEntiteiten().size(); i++) {
                        Entiteit entity = (Entiteit) getEntiteiten().get(i);
                        entity.move(delta);
                    }
                }

                getSpeelveld().draw(g); //moeten we afzonderlijk tekenen
                for (Entiteit entiteit : getEntiteiten()) {
                    entiteit.draw(g);
                }

                for (int p = 0; p < getEntiteiten().size(); p++) {
                    for (int s = p + 1; s < getEntiteiten().size(); s++) {
                        Entiteit ik = (Entiteit) getEntiteiten().get(p);
                        Entiteit hij = (Entiteit) getEntiteiten().get(s);

                        if (ik.collidesWith(hij)) {
                            ik.collidedWith(hij);
                            hij.collidedWith(ik);
                        }
                    }
                }
            }
            //Boodschap tekenen indien het spel "gepauseerd" is
            if (isVerderGaanKnopActieNodig()) {
                g.setFont(g.getFont().deriveFont(g.getFont().getStyle() ^ Font.BOLD));
                g.setColor(Color.BLUE);
                g.drawString(message, (800 - g.getFontMetrics().stringWidth(message)) / 2, 350);
                g.drawString("Druk op een knop om het spel te starten", (800 - g.getFontMetrics().stringWidth("Druk op een knop om het spel te starten")) / 2, 380);
            }

            // Buffer draaien en sprites tekenen
            g.dispose();
            strategy.show();

            // Paddle laten bewegen
            paddle.setHorizontalMovement(0);
            if ((isLinksGedrukt()) && (!isRechtsGedrukt())) {
                paddle.setHorizontalMovement(-paddleSnelheid);
            } else if ((isRechtsGedrukt()) && (!isLinksGedrukt())) {
                paddle.setHorizontalMovement(paddleSnelheid);
            }

            if (isSpatieKnop()) {
                //TODO: Actie uitvoeren afhankelijk van paddlestatus
            }

            try {
                Thread.sleep(8);
            } catch (Exception e) {
            }
        }
    }

    public static void main(String args[]) {
        Spel spel = new Spel();
        spel.gameLoop();
    }

    /** getters en setters **/
    /**
     * @return the linksGedrukt
     */
    public boolean isLinksGedrukt() {
        return linksGedrukt;
    }

    /**
     * @param linksGedrukt the linksGedrukt to set
     */
    public void setLinksGedrukt(boolean linksGedrukt) {
        this.linksGedrukt = linksGedrukt;
    }

    /**
     * @return the rechtsGedrukt
     */
    public boolean isRechtsGedrukt() {
        return rechtsGedrukt;
    }

    /**
     * @param rechtsGedrukt the rechtsGedrukt to set
     */
    public void setRechtsGedrukt(boolean rechtsGedrukt) {
        this.rechtsGedrukt = rechtsGedrukt;
    }

    /**
     * @return the spatieKnop
     */
    public boolean isSpatieKnop() {
        return spatieKnop;
    }

    /**
     * @param spatieKnop the spatieKnop to set
     */
    public void setSpatieKnop(boolean spatieKnop) {
        this.spatieKnop = spatieKnop;
    }

    /**
     * @return the verderGaanKnopActieNodig
     */
    public boolean isVerderGaanKnopActieNodig() {
        return verderGaanKnopActieNodig;
    }

    /**
     * @param verderGaanKnopActieNodig the verderGaanKnopActieNodig to set
     */
    public void setVerderGaanKnopActieNodig(boolean verderGaanKnopActieNodig) {
        this.verderGaanKnopActieNodig = verderGaanKnopActieNodig;
    }

    /**
     * @return the entiteiten
     */
    public List<Entiteit> getEntiteiten() {
        return entiteiten;
    }

    /**
     * @return the resterendeBallen
     */
    public int getResterendeBallen() {
        return resterendeBallen;
    }

    /**
     * @param resterendeBallen the resterendeBallen to set
     */
    public void setResterendeBallen(int resterendeBallen) {
        this.resterendeBallen = resterendeBallen;
        scorePaneel.setBallen(resterendeBallen);
    }

    /**
     * @return the extraBallen
     */
    public int getExtraBallen() {
        return extraBallen;
    }

    /**
     * @param extraBallen the extraBallen to set
     */
    public void setExtraBallen(int extraBallen) {
        this.extraBallen = extraBallen;
    }

    /**
     * @return the speelveld
     */
    public Speelveld getSpeelveld() {
        return speelveld;
    }

    /**
     * @param speelveld the speelveld to set
     */
    public void setSpeelveld(Speelveld speelveld) {
        this.speelveld = speelveld;
    }
}

class SluitenActie extends AbstractAction  {

    public SluitenActie(){
        super("Sluiten");
    }

    public void actionPerformed(ActionEvent e) {
        System.exit(0);
    }
}

class NieuwActie extends AbstractAction  {
    Spel spel;

    public NieuwActie(Spel spel){
        super("Nieuw spel");
        this.spel=spel;
    }

    public void actionPerformed(ActionEvent e) {
        spel.startGame();
    }
}